#include <QCoreApplication>
#include <stdio.h>
#include <stdlib.h>
#include <chrono> // std::chrono::seconds
#include <thread> // std::thread, std::this_thread::sleep_for
#include <QDebug>
#include <unistd.h> // sleep
#include <ctime> // std::time_t, std::tm, std::localtime, std::mktime
#include <time.h>
#include <stddef.h>

using namespace std;

/* POSIX线程库，这是传统C/C++程序员使用线程的方式。在C++ 11中，提供了语言层面使用线程的方式。
 *   最令人兴奋的是，Qt已经内置支持C++ 11，比如新建一个控制台项目后，可以在项目配置文件（.pro）中看到下面这一句：
 *   CONFIG+=c++11 console
 * C++ 11新标准中引入了如下5个头文件来支持多线程编程。
 *   ·　atomic：该头文件主要声明了两个类，即std::atomic 和std::atomic_flag，另外还声明了一套C语言风格的原子类型和与C语言兼容的原子操作函数。
 *   ·　thread：该头文件主要声明了std::thread类，另外std::this_thread命名空间也在该头文件中。
 *   ·　mutex：该头文件主要声明了与互斥量（mutex）相关的类，包括std::mutex系列类、std::lock_guard、std::unique_lock以及其他的类型和函数。
 *   ·　condition_variable：该头文件主要声明了与条件变量相关的类，包括std::condition_variable和std::condition_variable_any。
 *   ·　future：该头文件主要声明了std::promise和std::package_task这两个Provider 类，以及std::future和std::shared_future这两个Future类，
 *      另外还有一些与之相关的类型和函数，比如std::async函数。
 * std::thread类是非常重要的类，它的常用成员函数：
 *   thread        构造函数，有4种形式
 *   get_id        获得线程id
 *   joinable      判断线程对象是否可连接
 *   join          等待线程结束，是阻塞函数
 *   native_handle 用于获得与操作系统相关的原生线程句柄（需要本地库支持）
 *   swap          线程交换
 *   detach        分离线程
*/

/// 线程函数1
void thfunc1(int n)
{
    qDebug() << "thfunc1:" << n;
}
/// 线程函数2
void thfunc2()
{
    qDebug() << "i am c++11 thread func";
}
/// 定义结构体的类型
typedef struct
{
    int n;
    const char *str; // 注意这里要有const，否则会有警告
}MYSTRUCT;
void thfunc3(void *arg)
{
    MYSTRUCT *p = (MYSTRUCT*)arg;
    qDebug() << QString("in thfunc3:n=%1,str=%2").arg(p->n).arg(p->str); // 打印结构体内容
}
void thfunc4(int n,int m,int *pk,char s[])
{
    qDebug() << QString("in thfunc4:n=%1,m=%2,k=%3,str=%4").arg(n).arg(m).arg(*pk).arg(s);
    *pk = 5000; // 修改*pk
}
void thfunc5(int &n)
{
    qDebug() << "thfunc5:" << n;
    n += 20;
    this_thread::sleep_for(chrono::milliseconds(10)); // 等待10毫秒
}
thread::id main_thread_id = this_thread::get_id(); // 获取主线程id
void is_main_thread()
{
    if(main_thread_id == this_thread::get_id()) // 判断是否和主线程id相同
        qDebug() << "This is the main thread";
    else
        qDebug() << "This is not the main thread";
}
atomic<bool> ready(false); // 定义全局变量
void thfunc6(int id)
{
    while(!ready) // 一直等待，直到main主线程中重置全局变量ready
    {
        this_thread::yield(); // 让出自己的cpu时间
    }
    for(volatile int i=0; i<1000000; ++i) // 开始累加到100万
    {}
    qDebug() << id; // 累加完毕后打印本线程的序号，这样最终输出的才是排名，先完成先打印
}
/// 获取并打印当前时间
void getNowTime()
{
    timespec time;
    struct tm nowTime;
    clock_gettime(CLOCK_REALTIME, &time); // 获取相对于1970到现在的秒数
    localtime_r(&time.tv_sec, &nowTime);
    char current[1024];
    qDebug() << QString("%1-%2-%3 %4:%5:%6").arg(nowTime.tm_year+1900).arg(nowTime.tm_mon+1)
                    .arg(nowTime.tm_mday).arg(nowTime.tm_hour).arg(nowTime.tm_min).arg(nowTime.tm_sec);
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    /* 线程的创建
     * 在C++ 11中，创建线程的方式是用std::thread类的构造函数，std::thread类在#include<thread> 头文件中声明。
     * std::thread类的构造函数有4种形式：不带参数的默认构造函数、初始化构造函数、移动构造函数。
     * 虽然thread类的初始化可以提供这么丰富和方便的形式，但是其实现的底层依然是创建一个pthread线程并运行，有些实现甚至是直接调用pthread_create()来创建的。
     * 默认构造函数刚创建的thread对象，它的线程是不会马上运行的。
    */
    // 批量创建线程
    std::thread threads1[5]; // 批量创建5个thread对象，但此时这些线程并不会执行
    qDebug() << "create 5 threads...";
    for(int i=0; i<5; i++)
        threads1[i] = std::thread(thfunc1, i+1); // 这里开始执行线程函数thfunc1()
    for(auto &t : threads1)
        t.join(); // 等待每个线程结束
    qDebug() << "All threads joined";

    // 初始化构造函数
    /* 初始化构造函数是指是把线程函数的指针和线程函数的参数（如果有）都传入到线程类的构造函数中。这种形式最常用，由于传入了线程函数，因此定义线程对象的时候就会开始执行线程函数.
     * 创建好线程对象后，主线程会继续执行后面的代码，创建的子线程可能还没执行完主线程就结束了，比如控制台程序，主线程结束就意味着进程结束了。
     *   在这种情况下，我们需要让主线程等待，等待子线程全部运行结束后再继续执行主线程。还有一种情况，
     *   主线程为了统计各个子线程的工作结果而需要等待子线程结束完毕后再继续执行，此时主线程就要等待了。
     * 成员函数join()来等待子线程结束，即子线程线程函数执行后，join()函数才返回，因此join()是一个阻塞函数。
     *   函数join()会让主线程挂起（休眠，就是让出CPU），直到子线程都退出，同时join()能让子线程所占用的资源得到释放。子线程退出后，主线程会接收到系统的信号，从休眠中恢复。
    */
    // 创建一个不传参数的线程
    thread t2(thfunc2); // 定义线程对象，并传入线程函数指针
    sleep(1); // main线程挂起1秒钟，为了让子线程有机会执行

    /* 等待子线程结束没有调用sleep()函数，而是调用join()。
     *   sleep()函数只是等待一个固定的时间，有可能在这个固定的时间内子线程早已结束，或者子线程运行的时间大于这个固定时间，因此用它来等待子线程结束并不精确。
     *   用函数join()则会一直等到子线程结束后才执行该函数后面的代码。
    */
    // 创建一个线程，并传递结构体作为参数
    MYSTRUCT mystruct3; // 定义结构体
    mystruct3.n = 110;
    mystruct3.str = "hello world";
    thread t3(thfunc3,&mystruct3); // 定义线程对象t3，并传入结构体变量的地址
    t3.join(); // 等待线程对象t3结束

    // 创建一个线程，传多个参数给线程函数
    int n4=110,m4=200,k4=5;
    char str4[] = "hello world";
    thread t4(thfunc4,n4,m4,&k4,str4); // 定义线程对象，并传入多个参数
    t4.join();
    qDebug() << "k=" << k4; // 此时打印的应该是5000

    /* 前面提到，默认创建的线程都是可连接的线程，需要调用join()函数来等待该线程的结束并释放其占用的资源。
     *   除了以调用join()函数的方式来等待结束外，还可以调用成员函数detach()把可连接的线程分离。
     *   变成可分离的线程后，线程结束后就可以由系统自动回收资源，这样就不需要等待子线程结束，主线程可以自己先行结束。
    */
    // 把可连接的线程转为分离的线程（C++11和POSIX联合作战）
    thread t5(thfunc4,n4,m4,&k4,str4);
    sleep(1);
    t5.detach(); // 分离线程
    qDebug() << "k=" << k4; // 此时打印的应该是5000
//    pthread_exit(NULL); // 在主线程中执行“pthread_exit(NULL);”来结束主线程。main线程结束，但进程并不会结束，下面一句不会执行
//    qDebug() << "this line will not run"; // 不会执行
    // 在main线程中执行“pthread_exit(NULL);”的时候将结束main线程，但进程并不会立即退出，
    // 要等所有的线程全部结束后进程才会结束，所以能看到子线程中函数打印的内容。

    /* 通过移动构造函数的方式来创建线程是C++11创建线程的另一种常用方式。
     *   它通过向thread()构造函数中传入一个C++对象来创建线程。这种形式的构造函数定义如下：thread(thread&&x);
    */
    // 移动（move）构造函数->通过移动构造函数来启动线程
    int n5 = 0;
    qDebug() << "n5=" << n5;
    n5 = 10;
    thread t5_1(thfunc5,ref(n5)); // ref(n5)是取n5的引用
    thread t5_2(move(t5_1)); // t5_2执行thfunc5，t5_1不是thread对象
    t5_2.join(); // 等待t5_2执行完毕
    qDebug() << "n5=" << n5;
    // 从这个例子可以看出，t5_1并不会执行，执行的是t5_2，因为t5_1的线程函数移给t5_2了。

    /* 线程的标识符
     * 线程的标识符（id）可以用来唯一标识某个thread对象所对应的线程，以区别不同的线程。
     *   两个标识符相同的thread对象是同一个线程，或者还都没有线程。两个标识符不同的thread对象代表不同的线程，或者一个thread对象有线程、另一个没有。
     * id是线程标识符的类型，是thread类的成员，用来唯一标识某个线程。
     * 有时候，为了查看两个thread对象的id是否相同，可以在调试时把id打印出来。它们的数值虽然没有什么含义，但是可以比较是否相同，作为调试中的判断依据。
    */
    // 线程比较
    is_main_thread(); // is_main_thread作为main线程的普通函数来调用
    thread t6(is_main_thread); // is_main_thread作为线程函数来调用
    t6.join(); // 等待t6结束

    /* 当前线程this_thread
     * 在实际的线程开发中，经常需要访问当前线程。C++ 11提供了一个命名空间this_thread来引用当前线程，该命名空间集合了4个有用的函数：
     *   get_id()、yield()、sleep_until()、sleep_for()。函数get_id()和thread类的成员函数get_id()是同一个意思，都是用来获取线程id的。
     * 让出CPU时间
     *   调用函数yield()的线程将让出自己的CPU时间片，以便其他线程有机会运行，该函数的声明如下：void yield();调用该函数的线程放弃执行，回到就绪态。
    */
    // 为线程排名次
    thread threads6[10]; // 定义10个线程对象
    qDebug() << "race of 10 threads that count to 1 million:";
    for(int i=0; i<10; ++i)
        threads6[i] = thread(thfunc6,i); // 启动线程，把i当作参数传入线程函数，用于标记线程序号
    ready = true; // 重置全局变量
    for(auto &t6 : threads6)
        t6.join(); // 等待10个线程全部结束
    /* 线程刚刚启动的时候，都一直在while循环中让出自己的CPU时间，这就是函数yield()的作用。
     *   this_thread在子线程中使用，代表这个子线程。一旦跳出while循环，就开始累加，一直到加到100万，最后输出序号，全部序号输出后，得到跑完100万的排名.
     * atomic用来定义在全局变量ready上的操作都是原子操作。原子操作（后续章节会讲到）表示在多个线程访问同一个全局资源的时候，
     *   确保所有其他的线程都不在同一时间内访问这个相同的全局资源，也就是确保在同一时刻只有唯一的线程对这个全局资源进行访问。
     *   这有点类似于互斥对象对共享资源访问的保护，但是原子操作更加接近底层，因而效率更高。
    */

    /* 让线程暂停一段时间
     * 让线程暂停一段时间函数sleep_until()和sleep_for()用来阻塞线程，会让线程暂停执行一段时间。
    */
    // 暂停线程到下一分钟
    using std::chrono::system_clock;
    std::time_t tt = system_clock::to_time_t(system_clock::now());
    struct std::tm *ptm = std::localtime(&tt);
    getNowTime(); // 打印当前时间
    qDebug() << "Waiting for the netx minute to begin...";
    ++ptm->tm_min; // 累加一分钟
    ptm->tm_sec = 0; // 秒数置0
    // 暂停执行到下一个整分的时间点
    this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));
    getNowTime(); // 打印当前时间
    // main主线程到sleep_until()处开始挂起，然后到了下一个整分的时间（就是分钟加1，秒钟为0）再继续执行。

    // 暂停线程5秒
    qDebug() << "countdown:";
    for(int i=5; i>0; --i)
    {
        qDebug() << i;
        std::this_thread::sleep_for(std::chrono::seconds(1)); // 暂停一秒
    }
    qDebug() << "Lift off!";


    return a.exec();
}
